package com.myfoon.model.ejb;


import com.myfoon.model.cacheable.EventCache;
import com.myfoon.model.cacheable.PhotoCache;
import com.myfoon.model.cacheable.UserCache;
import com.myfoon.model.comparator.EventCacheComparator;
import com.myfoon.model.entity.Event;
import com.myfoon.model.entity.EventPhoto;
import com.myfoon.model.entity.Photo;
import com.myfoon.model.entity.User;
import com.myfoon.model.entity.result.EventOfFriend;
import com.myfoon.model.entryprocessor.EventAttachCounter;
import com.myfoon.model.entryprocessor.EventFinder;
import com.myfoon.model.entryprocessor.EventPhotosAdder;
import com.myfoon.model.entryprocessor.EventSubmitter;

import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;

import com.tangosol.util.InvocableMap.EntryProcessor;

import java.sql.Timestamp;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;


@Stateless(name = "EventEJB", mappedName = "EventEJB")
public class EventEJBBean implements EventEJBLocal, EventEJBRemote {
    @PersistenceContext(unitName = "MyfoonPU")
    EntityManager em;

    @EJB
    UserEJBLocal userLocal;

    public EventEJBBean() {
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public Event persist(Event event) {        
        Timestamp created = new Timestamp(new Date().getTime());
        event.setCreated(created);
        
        for (EventPhoto ep : event.getPhotos()) {
            ep.getPhoto().setCreated(created);
        }
        em.persist(event);
        
        NamedCache userInboxCacheService = CacheFactory.getCache("UserInbox");
        NamedCache userOutboxCacheService = CacheFactory.getCache("UserOutbox");
        NamedCache eventsCacheService = CacheFactory.getCache("Events");    
        NamedCache eventPhotosCacheService = CacheFactory.getCache("EventPhotos");
        NamedCache photosCacheService = CacheFactory.getCache("Photos");
        
        List<EventPhoto> eventPhotos = event.getPhotos();
        List<Long> eventPhotosList = new ArrayList<Long>();
        
        Map photoMap = new HashMap();        
        
        for (EventPhoto ep : eventPhotos) {            
            photoMap.put(ep.getPhoto().getId(), new PhotoCache(ep.getPhoto()));            
            eventPhotosList.add(ep.getPhoto().getId());
        }
                
        eventsCacheService.put(event.getId(), new EventCache(event));
        photosCacheService.putAll(photoMap);
        
        List<Long> friends = userLocal.findFriendIDs(event.getOwner());
        friends.add(new Long(event.getOwner().getId()));
                
        userInboxCacheService.invokeAll(friends, new EventSubmitter(event.getId(), 150));
        userOutboxCacheService.invoke(new Long(event.getOwner().getId()), new EventSubmitter(new Long(event.getId()), 0));
        eventPhotosCacheService.invoke(event.getId(), new EventPhotosAdder(eventPhotosList));
            
        return event;
    }

    public Event merge(Event event) {
        return null;
    }

    public void remove(Event event) {
    }

    public List<EventCache> getEvents(List<Long> eventIDs) {
        List<Long> eventIDsCopy = new ArrayList<Long>();
        eventIDsCopy.addAll(eventIDs);
        
        List<EventCache> eventCaches = new ArrayList<EventCache>();
        
        NamedCache cache = CacheFactory.getCache("Events");        
        Map map = cache.getAll(eventIDs);     
        
        for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
            Map.Entry entry = (Map.Entry)iter.next();
            EventCache ec = (EventCache)entry.getValue();
            eventCaches.add(ec);
            
            if (eventIDsCopy.contains(ec.getId())) {                
                eventIDsCopy.remove(ec.getId());
            }
        }         
        
        if (eventIDsCopy.size() > 0) {
            List<Event> events = new ArrayList<Event>();            
            
            Query q2 = em.createQuery("select e from Event e where e.id in (:id_sets)");
            q2.setParameter("id_sets", eventIDsCopy);
            events = q2.getResultList();            
            
            Map mapEvents = new HashMap();
            for (Event event : events) {
                EventCache ec = new EventCache(event);
                eventCaches.add(ec);
                mapEvents.put(ec.getId(), ec);
            }
            
            cache.putAll(mapEvents);            
        }             
        
        return eventCaches;
    }
    
    public List<EventOfFriend> getLastEvents(User user, Integer timezone, boolean friendsEvents, int start, int max) {
        long st = System.currentTimeMillis();        
        
        NamedCache inboxCache = null;
        if (friendsEvents) {           
            inboxCache = CacheFactory.getCache("UserInbox");
        }
        else {
            inboxCache  = CacheFactory.getCache("UserOutbox");    
        }
                
        
        
        List<Long> queryResults = (List<Long>)(inboxCache.invoke(user.getId(), new EventFinder(start, 15)));                
                        
        List<EventCache> eventCaches = getEvents(queryResults);                
        Collections.sort(eventCaches, new EventCacheComparator());
        
        List<Long> distinctEventOwners = new ArrayList<Long>();
        
        for (EventCache ec : eventCaches) {            
            if (!distinctEventOwners.contains(ec.getOwnerId())) {
                distinctEventOwners.add(ec.getOwnerId());                
            }            
        }        
        
        Map userCacheMap = userLocal.getUsersMap(distinctEventOwners);
        
        List<EventOfFriend> events = new ArrayList<EventOfFriend>();                                
                        
        NamedCache eventPhotos = CacheFactory.getCache("EventPhotos");
        Map eventPhotosCountMap = eventPhotos.invokeAll(queryResults, new EventAttachCounter());                
        
        for (EventCache ec : eventCaches) {                        
            EventOfFriend eof = new EventOfFriend(ec, (UserCache)userCacheMap.get(ec.getOwnerId()), null);
            eof.setPhotoCount((Integer)eventPhotosCountMap.get(ec.getId()));            
            events.add(eof);
        }   
        
        System.out.println("Estimated Time: " + (System.currentTimeMillis()-st));
        return events;
    }
}
